Except where otherwise noted, this content is Copyright (c) 2020, RTE and licensed under a CC-BY-4.0 license.

Network Investment

Welcome to this new tutorial. Off course Hadar is well designed to compute study for network adequacy. You can launch Hadar to compute adequacy for the next second or next year.

But Hadar can also be used like a asset investment tool. In this example, thanks to Hadar, we will make the best choice for renewable energy and network investment.

We have a small region, with metropole which doesn’t produce anything, a nuclear plan and two small cities with production.

First step parse data with pandas (and plot them)

import numpy as np
import pandas as pd
import hadar as hd
import plotly.graph_objects as go

Input data

a = pd.read_csv('a.csv', index_col='date')
fig = go.Figure()
fig.add_traces(go.Scatter(x=a.index, y=a['consumption'], name='load'))
fig.add_traces(go.Scatter(x=a.index, y=a['gas'], name='gas'))
fig.update_layout(title_text='Node A', yaxis_title='MW')
b = pd.read_csv('b.csv', index_col='date')
fig = go.Figure()
fig.add_traces(go.Scatter(x=b.index, y=b['consumption'], name='load'))
fig.update_layout(title_text='Node B (only consumption)', yaxis_title='MW')
c = pd.read_csv('c.csv', index_col='date')
fig = go.Figure()
fig.add_traces(go.Scatter(x=c.index, y=c['nuclear'], name='load'))
fig.update_layout(title_text='Node C (only production)', yaxis_title='MW')
d = pd.read_csv('d.csv', index_col='date')
fig = go.Figure()
fig.add_traces(go.Scatter(x=d.index, y=d['consumption'], name='load'))
fig.add_traces(go.Scatter(x=d.index, y=d['eolien'], name='eolien'))
fig.update_layout(title_text='Node D', yaxis_title='MW')

Base Study

Next step, code this network with Hadar

line = np.ones(8760) * 2000 # 2000 MW
base = hd.Study(horizon=8760)\
    .network()\
        .node('a')\
            .consumption(name='load', cost=10**6, quantity=a['consumption'])\
            .production(name='gas', cost=80, quantity=a['gas'])\
        .node('b')\
            .consumption(name='load', cost=10**6, quantity=b['consumption'])\
        .node('c')\
            .production(name='nuclear', cost=50, quantity=c['nuclear'])\
        .node('d')\
            .consumption(name='load', cost=10**6, quantity=d['consumption'])\
            .production(name='eolien', cost=20, quantity=d['eolien'])\
        .link(src='a', dest='b', cost=5, quantity=line)\
        .link(src='b', dest='c', cost=5, quantity=line)\
        .link(src='c', dest='a', cost=5, quantity=line)\
        .link(src='c', dest='b', cost=10, quantity=line)\
        .link(src='c', dest='d', cost=10, quantity=line)\
        .link(src='d', dest='c', cost=10, quantity=line)\
    .build()
optimizer = hd.LPOptimizer()
def compute_cost(study):
    res = optimizer.solve(study)
    agg = hd.ResultAnalyzer(study=study, result=res)
    return agg.get_cost().sum(axis=1), res.benchmark
def print_bench(bench):
    print('mapper', bench.mapper)
    print('modeler', sum(bench.modeler))
    print('solver', sum(bench.solver))
    print('total', bench.total)
base_cost, bench = compute_cost(base)
base_cost = base_cost[0]

Find best place for solar

An investissor want to build a solar park with solar cells. According to last last data meteo, he could except the amount of production from this park. (Solar radiation is the same on each node of network).

What is the best node to install these solar pans ? (B is excluded because there are not enough space)

park = pd.read_csv('solar.csv', index_col='date')
fig = go.Figure()
fig.add_traces(go.Scatter(x=park.index, y=park['solar'], name='solar'))
fig.update_layout(title_text='Forecast Solar Park Power', yaxis_title='MW')

We can build one study for each different scenarios. However, Hadar can compute many scenarios at once for a more efficient compute. Result are the same. The possibility to compute many scenarios at once, is very important for next topic Stochastic Study.

def build_sparce_data(total: int, at, data) -> np.ndarray:
    """
    Build many scenarios input where all scenario is empty but one.
    :param total: number of scenario to generate
    :param at: scenario index to fill
    :param data: data to fill in selected scenario

    :return: matrix with shape (nb_scn, horizon) where only one scenario is not zero.
    """
    if isinstance(data, pd.DataFrame):
        data = data.values.flatten()
    sparce = np.ones((total, data.size))
    sparce[at, :] = data
    return sparce

We use start three studies one for each node.

solar = hd.Study(horizon=8760, nb_scn=3)\
    .network()\
        .node('a')\
            .consumption(name='load', cost=10**6, quantity=a['consumption'])\
            .production(name='gas', cost=80, quantity=a['gas'])\
            .production(name='solar', cost=10, quantity=build_sparce_data(total=3, at=0, data=park))\
        .node('b')\
            .consumption(name='load', cost=10**6, quantity=b['consumption'])\
        .node('c')\
            .production(name='nuclear', cost=50, quantity=c['nuclear'])\
            .production(name='solar', cost=10, quantity=build_sparce_data(total=3, at=1, data=park))\
        .node('d')\
            .consumption(name='load', cost=10**6, quantity=d['consumption'])\
            .production(name='eolien', cost=20, quantity=d['eolien'])\
            .production(name='solar', cost=10, quantity=build_sparce_data(total=3, at=2, data=park))\
        .link(src='a', dest='b', cost=5, quantity=line)\
        .link(src='b', dest='c', cost=5, quantity=line)\
        .link(src='c', dest='a', cost=5, quantity=line)\
        .link(src='c', dest='b', cost=10, quantity=line)\
        .link(src='c', dest='d', cost=10, quantity=line)\
        .link(src='d', dest='c', cost=10, quantity=line)\
    .build()
costs, bench = compute_cost(solar)
costs = pd.Series(data=costs, name='cost', index=['a', 'c', 'd'])
(base_cost - costs) / base_cost * 100
a    8.070145
c    2.342062
d    2.736793
Name: cost, dtype: float64

As we can see, network is more efficient if solar park is installed one node A (8% more efficient than only 2-3% for other node)

Find best place with on more line

Add an extra difficulties ! Region want to invest in a new line between A->C, D->B, A->D, D->A.

In this case, What is the best place to install solar park and what is the more usefull line to build ?

solar_line = hd.Study(horizon=8760, nb_scn=12)\
    .network()\
        .node('a')\
            .consumption(name='load', cost=10**6, quantity=a['consumption'])\
            .production(name='gas', cost=80, quantity=a['gas'])\
            .production(name='solar', cost=10, quantity=build_sparce_data(total=12, at=[0, 3, 6, 9], data=park))\
        .node('b')\
            .consumption(name='load', cost=10**6, quantity=b['consumption'])\
        .node('c')\
            .production(name='nuclear', cost=50, quantity=c['nuclear'])\
            .production(name='solar', cost=10, quantity=build_sparce_data(total=12, at=[1, 4, 7, 10], data=park))\
        .node('d')\
            .consumption(name='load', cost=10**6, quantity=d['consumption'])\
            .production(name='eolien', cost=20, quantity=d['eolien'])\
            .production(name='solar', cost=10, quantity=build_sparce_data(total=12, at=[2, 5, 8, 11], data=park))\
        .link(src='a', dest='b', cost=5, quantity=line)\
        .link(src='b', dest='c', cost=5, quantity=line)\
        .link(src='c', dest='a', cost=5, quantity=line)\
        .link(src='c', dest='b', cost=10, quantity=line)\
        .link(src='c', dest='d', cost=10, quantity=line)\
        .link(src='d', dest='c', cost=10, quantity=line)\
        .link(src='a', dest='c', cost=10, quantity=build_sparce_data(total=12, at=[0, 1, 2], data=line))\
        .link(src='d', dest='b', cost=10, quantity=build_sparce_data(total=12, at=[3, 4, 5], data=line))\
        .link(src='a', dest='d', cost=10, quantity=build_sparce_data(total=12, at=[6, 7, 8], data=line))\
        .link(src='d', dest='a', cost=10, quantity=build_sparce_data(total=12, at=[9, 10, 11], data=line))\
    .build()
costs2, bench = compute_cost(solar_line)
costs2 = pd.DataFrame(data=costs2.reshape(4, 3),
                      index=['a->c', 'd->b', 'a->d', 'd->a'], columns=['a', 'c', 'd'])
(base_cost - costs2) / base_cost * 100
a c d
a->c 8.445071 2.742256 3.128567
d->b 47.005792 45.569439 47.355664
a->d 9.307269 3.527078 3.642307
d->a 46.994315 45.557806 47.343787

Very interesting, new line is a game changer. D->A and D->B seem most valuable lines. If D->B is created, it’s more efficient to install solar park on node D !